<?php
declare(strict_types=1);

namespace SixShop\WechatPay\Trait;

use GuzzleHttp\Promise\PromiseInterface;
use SixShop\WechatPay\Config;
use WeChatPay\BuilderChainable;
use WeChatPay\Crypto\Rsa;
use function SixShop\Core\throw_logic_exception;

trait ApiTrait
{
    use HandleAsyncRequestTrait;

    /**
     * 微信支付
     *
     * @param string $openid 用户openid
     * @param string $outTradeNo 商户订单号
     * @param int $total 订单总金额，单位为分
     * @param string $description 商品描述
     * @param string $notifyUrl 通知地址
     * @param int $expireTime 订单过期时间戳
     * @return mixed
     */
    private function wechatPay(string $openid, string $outTradeNo, int $total, string $description, int $expireTime)
    {
        if ($total <= 0) {
            throw_logic_exception('不支持的支付金额');
        }
        // https://pay.weixin.qq.com/doc/v3/merchant/4012791856
        // 【POST】/v3/pay/transactions/jsapi
        return $this->handleAsyncRequest(function (
            BuilderChainable $builder, Config $config, string $openid, string $outTradeNo, int $total, string $description, int $expireTime
        ): PromiseInterface {
            return $builder->v3->pay->transactions->jsapi->postAsync([
                'json' => [
                    'mchid' => $config->mchid,
                    'out_trade_no' => $outTradeNo,
                    'appid' => $config->appid,
                    'description' => $description,
                    'notify_url' => $config->notify_url,
                    // yyyy-MM-DDTHH:mm:ss+TIMEZONE
                    'time_expire' => date('Y-m-d\TH:i:s+08:00', $expireTime),
                    'attach' => 'actor',
                    'amount' => [
                        'total' => $total,
                        'currency' => 'CNY'
                    ],
                    'payer' => [
                        'openid' => $openid
                    ]
                ],
            ]);
        }, $openid, $outTradeNo, $total, $description, $expireTime);

    }

    /**
     * 商户订单号查询订单
     */
    private function queryByOutTradeNo(string $outTradeNo)
    {
        // https://pay.weixin.qq.com/doc/v3/merchant/4012791900
        // 【GET】/v3/pay/transactions/out-trade-no/{out_trade_no}
        return $this->handleAsyncRequest(function (BuilderChainable $builder, Config $config, string $outTradeNo): PromiseInterface {
            return $builder->v3->pay->transactions->outTradeNo->_out_trade_no_->getAsync([
                'query' => [
                    'mchid' => $config->mchid,
                ],
                'out_trade_no' => $outTradeNo,
            ]);
        }, $outTradeNo);
    }

    /**
     * 退款申请
     *
     * @param string $outRefundNo 商户退款单号
     */
    private function domesticRefunds(string $outRefundNo, string $outTradeNo, float $refund, float $total = null, string $reason = '交易取消')
    {
        $total = $total ?? $refund;
        // https://pay.weixin.qq.com/doc/v3/merchant/4012791862
        // 【POST】/v3/refund/domestic/refunds
        return $this->handleAsyncRequest(function (BuilderChainable $builder, Config $config, $outRefundNo, $outTradeNo, $refund, $total, $reason): PromiseInterface {
            return $builder->v3->refund->domestic->refunds->postAsync([
                'json' => [
                    'out_refund_no' => $outRefundNo,
                    'out_trade_no' => $outTradeNo,
                    'reason' => $reason,
                    'amount' => [
                        'refund' => (int)($refund * 100),
                        'total' => (int)($total * 100),
                        'currency' => 'CNY'
                    ],
                    'notify_url' => $config->refund_notify_url
                ]
            ]);
        }, $outRefundNo, $outTradeNo, $refund, $total, $reason);
    }

    private function queryRefund(string $outRefundNo)
    {
        // https://pay.weixin.qq.com/doc/v3/merchant/4012791863
        // 【GET】/v3/refund/domestic/refunds/{out_refund_no}
        return $this->handleAsyncRequest(function (BuilderChainable $builder, Config $config, string $outRefundNo): PromiseInterface {
            return $builder->v3->refund->domestic->refunds->_out_refund_no_->getAsync([
                'out_refund_no' => $outRefundNo,
            ]);
        }, $outRefundNo);
    }

    /**
     * 发起转账
     *
     * @param int $openid 用户openid
     * @param string $orderSn 订单号
     * @param string $amount 转账金额
     * @return mixed
     * @deprecated 这个只能支持佣金报酬，请用transferBill
     * @see transferBill
     */
    private function transferBills(int $openid, string $orderSn, string $amount)
    {
        // https://pay.weixin.qq.com/doc/v3/merchant/4012716434
        //【POST】/v3/fund-app/mch-transfer/transfer-bills
        return $this->handleAsyncRequest(function (BuilderChainable $builder, Config $config) use ($openid, $orderSn, $amount): PromiseInterface {
            return $builder->v3->fundApp->mchTransfer->transferBills->postAsync([
                'json' => [
                    'appid' => $config->appId,
                    'openid' => $openid,
                    'out_bill_no' => $orderSn,
                    'transfer_amount' => (int)($amount * 100),
                    'notify_url' => $config->notifyUrl,
                    'transfer_scene_id' => '1005', // 佣金报酬
                    'transfer_scene_report_infos' => [
                        [
                            'info_type' => '岗位类型',
                            'info_content' => '合作运营'
                        ],
                        [
                            'info_type' => '报酬说明',
                            'info_content' => sprintf('提现到账%.2f元', $amount)
                        ],
                    ],
                    'transfer_remark' => sprintf('提现%s元到零钱', $amount),
                ]
            ]);
        });
    }

    /**
     * 发起转账
     */
    private function transferBill(
        string $outBillNo,
        string $transferSceneId,
        String $openid,
        int $transferAmount,
        String $transferRemark,
        array $transferSceneReportInfos,
        string $userName = '',
        string $userRecvPerception = ''
    ): object
    {
        // https://pay.weixin.qq.com/doc/v3/merchant/4012716434
        // 支持商户：【普通商户】
        // 请求方式：【POST】/v3/fund-app/mch-transfer/transfer-bills
        // appid
        // notify_url
        $data =  [
            'out_bill_no' => $outBillNo,
            'transfer_scene_id' => $transferSceneId,
            'openid' => $openid,
            'transfer_amount' => $transferAmount,
            'transfer_remark' => $transferRemark,
            'transfer_scene_report_infos' => $transferSceneReportInfos,
        ];
        if ($userName) {
            $encryptor = static function(string $msg): string {
                return Rsa::encrypt($msg, $this->config->public_key);
            };
            $data['user_name'] = $encryptor($userName);
        }
        if ($userRecvPerception) {
            $data['user_recv_perception'] = $userRecvPerception;
        }
        return $this->handleAsyncRequest(function (
            BuilderChainable $builder, Config $config, $data
        ): PromiseInterface {
            $data['appid'] = $config->appid;
            $data['notify_url'] = $config->notify_url;
            return $builder->v3->fundApp->mchTransfer->transferBills->postAsync([
                'json' => $data,
                'headers' => [
                    'Wechatpay-Serial' => $config->public_key_id,
                ]
            ]);
        }, $data);
    }

    private function queryTransferBill(string $outBillNo)
    {
        // https://pay.weixin.qq.com/doc/v3/merchant/4012716437
        //【GET】/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{out_bill_no}
        return $this->handleAsyncRequest(function (BuilderChainable $builder, Config $config, string $outBillNo): PromiseInterface {
            return $builder->v3->fundApp->mchTransfer->transferBills->outBillNo->_out_bill_no_->getAsync([
                'out_bill_no' => $outBillNo,
            ]);
        }, $outBillNo);
    }
}
